package com.ideal.service.krb;

import com.ideal.service.Utils;
import com.ideal.tools.db.MysqlDBUtils;
import com.ideal.tools.ssh.common.CommonTools;
import com.ideal.tools.ssh.common.OperationMarket;
import com.ideal.tools.ssh.common.PropertyBox;
import com.ideal.tools.ssh.common.PropertyDictory;
import com.ideal.tools.ssh.context.ClusterContext;
import com.ideal.tools.ssh.entity.LinuxMachine;
import com.ideal.tools.ssh.executor.SSHExecutor;
import com.ideal.tools.ssh.result.LinuxResult;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.*;

/**
 * Created by CC on 2016/7/25.
 */
public class KerberosService {

    private static Logger logger = LoggerFactory.getLogger(KerberosService.class);

    //这里是所有的机器信息 提供个下面的用户
    Map<String,LinuxMachine> linuxMachines;
    private static final String  LinuxMachineKey="LinuxMachineKey";
    public static final String START_KERBEROS_REFRESH = "start_kerberos_refresh";  //解析时使用
    public static final String END_KERBEROS_REFRESH = "end_kerberos_refresh";   //解析时使用
    private static final String kerberos_exe_file = "kerberos_exe_file";
    private static final String kerberos_flush_file = "kerberos_flush_file";


    private String exe_file_path;
    private String flush_file_path;
    private String shell_path;
    private String ticket_path;

    public KerberosService(){
        //获取一份机器信息
        linuxMachines = Utils.getTotalMachineInfo();
    }

    /**
     * 这个用来刷新kerberos 并复制，一份
     * 其实这里的刷新krb 方式不是很好
     * 现在先用这总生成日志解析的方式 后面需要改进
     */
    public void KerberosFresh(){
        KerberosFresh("");
    }

    public void KerberosFresh(String appendWhere){
        System.out.println("===============start fresh kerberos======================");

        //初始化配置信息
        initFileInfo();
//        initFileInfo_151();
        //载入kerberos配置数据
        List<Map<String,Object>> configInfo=loadKerberosConfig(appendWhere);
        if(configInfo==null||configInfo.size()==0)return;
        //拼接shell文件内容
        StringBuffer cmd= assembleShellCMD(configInfo);
        //根据上面的命令生成执行文件
        System.out.println("start write cmd!");
        Utils.writeLinuxFile(exe_file_path,cmd);
        //执行执行文件，这个地方文件的生成是在wepapp机器上，执行也在这个机器上
        Utils.exeLocalCMD("sh "+exe_file_path);
        //解析运行命令生成的日志文件 并清空数据库表，同时插入新数据
        List<String[]> authResults=parseLogFile();
        //复制备份

        copyKeyTabFileToLocal(authResults);

        System.out.println("===============end fresh kerberos======================");
    }

    private void initFileInfo(){
        //先初始化上个文件放置的地址
        shell_path=PropertyBox.getVal(PropertyDictory.WEBAPP_SHELL_PATH,""); //sh脚本位置
        String init_path=PropertyBox.getVal(PropertyDictory.WEBAPP_INIT_PATH,"");//初始位置
        ticket_path= PropertyBox.getVal(PropertyDictory.Ticket_Cache_Path,""); //复制脚本位置
        if(!init_path.endsWith("/"))
            init_path=init_path+"/";
        if (!shell_path.endsWith("/"))
            shell_path = shell_path+"/";
        if(!ticket_path.endsWith("/"))
            ticket_path=ticket_path+"/";
        long time=System.currentTimeMillis();

        exe_file_path =  init_path+kerberos_exe_file+time+".sh";
        flush_file_path = init_path+kerberos_flush_file+time+".txt";
    }

    private void initFileInfo_151(){


        //先初始化上个文件放置的地址
        shell_path=PropertyBox.getVal(PropertyDictory.WEBAPP_SHELL_PATH,""); //sh脚本位置
        String init_path=PropertyBox.getVal(PropertyDictory.WEBAPP_INIT_PATH,"");//初始位置
        ticket_path= PropertyBox.getVal(PropertyDictory.Ticket_Cache_Path,""); //复制脚本位置
        if(!init_path.endsWith("/"))
            init_path=init_path+"/";
        if (!shell_path.endsWith("/"))
            shell_path = shell_path+"/";
        if(!ticket_path.endsWith("/"))
            ticket_path=ticket_path+"/";
        long time=System.currentTimeMillis();

        exe_file_path =  "D:\\tmp/"+kerberos_exe_file+time+".sh";
        flush_file_path = "D:\\tmp/"+"kerberos_flush_file1470339183213.txt";
//        flush_file_path = init_path+kerberos_flush_file+time+".txt";

    }

    /**
     * 加载mysql 中的配置数据
     */
    private List<Map<String,Object>> loadKerberosConfig(String appendWhere){
        //查询出需要刷新，并且在有效期内的数据
        String sql = "select userinfo.userName username,userinfo.systemPW pwd, config.machineIds ids " +
                "from cluster_user_kbrconfig config left join cluster_user userinfo on config.userId=userinfo.id " +
                "where config.status=0 and (config.validDay=0 or config.endTime>UNIX_TIMESTAMP()*1000)";
        if(appendWhere!=null&&appendWhere.trim().length()>0){
            sql=sql+" "+appendWhere;
        }
        System.out.println("sql: "+sql);
        List<Map<String,Object>> rows= MysqlDBUtils.queryWebDB(sql);

        List<Map<String,Object>> configMap=new ArrayList<Map<String, Object>>();
        //一条数据变多条(1,2,3,4-->1  2  3  4)变4条
        for (Map<String,Object> row:rows){
            String ids=row.get("ids").toString();
            for(String id:ids.split(",")){
                Map<String,Object> oneConfig=new HashMap<String, Object>();
                oneConfig.putAll(row);//加入原始数据
                oneConfig.put(LinuxMachineKey, linuxMachines.get(id));//放入机器信息
                oneConfig.put("MachineId",id);
                configMap.add(oneConfig);
                System.out.println("machine lust:"+linuxMachines.size()+":clinet info :"+oneConfig.get(LinuxMachineKey));

            }
        }
        return configMap;
    }

    private StringBuffer assembleShellCMD(List<Map<String,Object>> configInfo){
        StringBuffer cmd = new StringBuffer();
        cmd.append("#!/bin/bash" + "\n");      //文件头
        StringBuffer param = new StringBuffer();
        String kdc_realm= PropertyBox.getVal(PropertyDictory.KDC_REALM,"");
        System.out.println("====config size===:"+configInfo.size());
        for (Map<String,Object> oneConfig:configInfo){
            LinuxMachine machine=(LinuxMachine)oneConfig.get(LinuxMachineKey);
            String authUser=oneConfig.get("username").toString(); //被认证的用户
            String kdcPassWord=oneConfig.get("pwd").toString(); //kdc认证密码
            String host= machine.getSshAuthor().getHost();   //需要认证的机器
            String logUser = machine.getSshAuthor().getUsername();//认证机器的登陆用户
            String passwd= machine.getSshAuthor().getPasswd(); //认证机器的登陆密码
            String kdc_key= authUser + kdc_realm;
            param.append(host).append(" ");
            param.append("'").append(logUser).append("'").append(" ");
            param.append("'").append(passwd).append("'").append(" ");
            param.append("'").append(authUser).append("'").append(" ");
            param.append("'").append(kdc_key).append("'").append(" ");
            param.append("'").append(kdcPassWord).append("'").append(" ");
            cmd.append("echo -e \"\\n" + START_KERBEROS_REFRESH + ":" + host
                    + "," + authUser + "," + kdc_key + ":param_end\">>" + flush_file_path + ";" +
                    "expect " + shell_path + "authClientPrinc.exp " + param.toString() + ">>" + flush_file_path + ";" +
                    "echo -e \"\\n" + END_KERBEROS_REFRESH + "\\n\">>" + flush_file_path + "\n");
            param.setLength(0);
        }
        System.out.println("====cmd===:"+cmd.toString());
        return cmd;
    }

    /**
     * 解析日志
     * 把数据存取数据库
     */
    private List<String[]> parseLogFile(){
        System.out.println("file :"+flush_file_path);

        //读取日志信息
        List<String> logInfo = Utils.readFile2List(flush_file_path);

        boolean started = false;
        //开始解析
        String username="",userid="",machineip="",machineid="",starttime="",
                endtime="",principal="",ticketpath="",status="0";
        boolean hastime=false;
        Map<String,String> userMap = Utils.getUserMapReserve(); //username-->id
        Map<String,String> machineMap = Utils.getCltReservMap(); //machineip--> id

        List<String[]> params=new ArrayList<String[]>(); //存放inster 参数
        List<String[]> allparams=new ArrayList<String[]>();//存放所有参数，给后面的cp 操作使用
        String webIP = getMachineByType(linuxMachines, LinuxMachine.MachineType.WebAPP).getSshAuthor().getHost();
        for(String line:logInfo){
            if (StringUtils.isEmpty(line)) continue;
            if(line.startsWith(START_KERBEROS_REFRESH)){
                //start_kerberos_refresh:10.5.24.151,u_hulianwangbu_1,u_hulianwangbu_1@EXAMPLE.COM:param_end
                started = true;
                String[] vals=line.split(":")[1].split(",");
                machineip=vals[0];
                username=vals[1];
                principal=vals[2];
            }else if(line.startsWith(END_KERBEROS_REFRESH)){
                started=false;
                userid=userMap.get(username);
                machineid=machineMap.get(machineip);

                //收集一条数据
                String[] vals = new String[]{userid,machineid,starttime,endtime,principal,ticketpath,status,System.currentTimeMillis()+""};
                //这里是提供给下面的方法，这里直接便被出 在远端执行还是在web端执行
                String isLocal="local";
                if (!webIP.equals(machineip))
                    isLocal="remote";
                String[] allVals= new String[]{userid,username,machineid,machineip,starttime,endtime,principal,ticketpath,status,isLocal};
                params.add(vals);
                allparams.add(allVals);
                //清空数据
                username="";userid="";machineip="";machineid="";
                starttime="";endtime="";principal="";ticketpath="";status="0";
            }

            if (started) {
                //替换参数
                //status 0成功 1失败
                if (line.contains("Password incorrect")) {
                    status = "1";
                }
                //path
                if (line.contains("Ticket cache:")) {
                    //Ticket cache: FILE:/tmp/krb5cc_916
                    String[] tmp = line.split(":");
                    if (tmp.length >= 3) ticketpath = tmp[2].trim();
                }
                //start end
                if (line.contains("Valid starting")) {
                    hastime = true;
                }
                if (hastime && !line.contains("Valid starting")) {
                    //07/27/16 01:00:02  07/30/16 01:00:02  krbtgt/EXAMPLE.COM@EXAMPLE.COM^M
                    String[] tmp = line.split(" ");
                    if (tmp.length >= 5) {
                        starttime = Utils.parseStringToDate(tmp[0] + " " + tmp[1],"MM/dd/yy HH:mm:ss");
                        endtime = Utils.parseStringToDate(tmp[3] + " " + tmp[4],"MM/dd/yy HH:mm:ss");
                    } else {
                        status = "1";
                    }
                    hastime = false;
                }
            }

        }


        //插入数据库
        String sql="insert into cluster_user_kbrauth " +
                "(userid,machineid,starttime,endtime,principal,ticketpath,status,createtime) " +
                "values (?,?,?,?,?,?,?,?)";
        String delSql="delete from cluster_user_kbrauth where userid=? and machineid=?";
        Object[][] o=new Object[params.size()][];
        Object[][] delParams = new Object[params.size()][];
        for(int i=0;i<params.size();i++){
            //插入先删除
            String[] strTmp=new String[]{params.get(i)[0],params.get(i)[1]};
            delParams[i]= strTmp;
            o[i]=params.get(i);
        }

        MysqlDBUtils.batchWebDB(delSql,delParams);
        MysqlDBUtils.batchWebDB(sql,o);

        return allparams;
    }


    /**
     * 复制各个机器上的文件到本地
     *
     * userid,username,machineid,machineip,starttime,endtime,principal,ticketpath,status,"remote/local"
     *
     */
    private void copyKeyTabFileToLocal(List<String[]> authResults){

        if(linuxMachines==null||linuxMachines.size()==0) {
            linuxMachines=Utils.getTotalMachineInfo();
        }
        String lastKey="";
        boolean lastSucc=false;
        List<String[]> finalList = initCopyInfo(authResults);

        for(String[] authInfo:finalList){
            String machineid=authInfo[2];
            String desPath=authInfo[7];
            String username=authInfo[1];
            String ticket= ticket_path+username;
            String curKey= username;
            String isLocal= authInfo[9];
            System.out.println(machineid+"-"+desPath+"-"+username+"-"+curKey+"-"+isLocal);
            if (curKey.equals(lastKey) && lastSucc) {
                //上一条和这条是同一个用户，并且上一条下载成功，则我们不在重复处理了
                continue;
            }else{
                if ("local".equals(isLocal)){
                    String cmd1 = "sudo cp "+desPath+" "+ticket;
                    String cmd2 = "sudo chmod "+"o+r "+ticket;

                    int rs1=Utils.exeLocalSudoCMD(cmd1);
                    int rs2=Utils.exeLocalSudoCMD(cmd2);

                    System.out.println("local exe:[" + cmd1+ "]  result:[" + rs1 + "]["+ cmd2+ "]  result:[" + rs2 + "]");
                    lastSucc = rs1 ==0 &&rs2==0;

                }else if("remote".equals(isLocal)) {
                    LinuxMachine machine = linuxMachines.get(machineid);
                    LinuxMachine newmachine =makeANewClientMachine(machine,username);
                    LinuxResult result = (LinuxResult) CommonTools.downLoadFileToLocal(newmachine.getSshAuthor(), desPath, ticket);
                    System.out.println("remote exe:[" + result.getCmd() + "]  result:[" + result.getExitCode() + "]");
                    lastSucc = result.getExitCode() == 0;
                }else{
                    lastSucc = false;
                }
            }
            lastKey=curKey;
        }
    }

    private LinuxMachine makeANewClientMachine(LinuxMachine machine ,String username){
        String sql = "select clientpw from cluster_user where username='"+username+"'";
        List<Map<String,Object>> rows=MysqlDBUtils.queryWebDB(sql);
        String passwd="";
        if(rows!=null&&rows.size()>0){
            passwd = rows.get(0).get("clientpw").toString();
        }
        System.out.println("change linux machine to username and passwd :["+username+"]["+passwd+"]");
        LinuxMachine newmachine =  new LinuxMachine();
        newmachine.initLoginName(username).initPassWord(passwd).initIP(machine.getSshAuthor().getHost()) ;
        return newmachine;
    }

    /**
     * 处理下数据
     * userid,username,machineid,machineip,starttime,endtime,principal,ticketpath,status,"remote/local"
     *
     * status =0 的才处理
     * 排序 根据 machine+local/remote
     * 分辨
     * @param authResults
     */
    private List<String[]> initCopyInfo(List<String[]> authResults){
        List<String[]> finalList = new ArrayList<String[]>();

        for(String[] vals: authResults){
            System.out.println("sart sort:"+ vals.length+":"+vals[0]+"-"+vals[8]);

            String status = vals[8];
            if (status.equals("1"))
                continue;
            finalList.add(vals);
        }
        System.out.println("sart sort");
        return sortList(finalList);
    }

    /**
     * 这个只能获取一条机器 ，如果一个类型有多个机器则不能使用
     * @param machineMap
     * @param type
     * @return
     */
    private LinuxMachine getMachineByType(Map<String,LinuxMachine> machineMap,LinuxMachine.MachineType type){
        for(Map.Entry<String,LinuxMachine> entry:machineMap.entrySet()){
            if(entry.getValue().getMachineType().equals(type))
                return entry.getValue();
        }

        return null;
    }

    /**
     * 执行本地命令
     * @param cmd
     * @return
     */
    private int execShell(String cmd) {
        StringBuffer cmdout = new StringBuffer();
        try {
            System.out.println("cmd:"+cmd);
            Process process = Runtime.getRuntime().exec(cmd);     //执行一个系统命令
            InputStream fis = process.getInputStream();
            BufferedReader br = new BufferedReader(new InputStreamReader(fis));
            String line = null;

            while ((line = br.readLine()) != null) {
                cmdout.append(line).append(System.getProperty("line.separator"));
            }

            return  process.exitValue();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return -1;
    }

    /**
     * 对list 排序
     * userid,username,machineid,machineip,starttime,endtime,principal,ticketpath,status,"remote/local"
     *
     * 根据 machineid + "remote/local" 排序
     *
     * @param list
     */
    private List<String[]> sortList(List<String[]> list){
        //这里使用map打包 然后排序"remote/local"；
        Map<String , List<String[]>> map=new HashMap<String, List<String[]>>();

        List<String[]> finalList=new ArrayList<String[]>();
        for (String[] row:list){
            String id=row[1];
            List<String[]> lrow=map.get(id) ;
            if(lrow==null){
                lrow=new ArrayList<String[]>();
                map.put(id,lrow);
            }
            lrow.add(row);
        }

        for (Map.Entry<String, List<String[]>> entry : map.entrySet()){
            List<String[]> llist = entry.getValue();
            Collections.sort(llist,new MyComparator());
            finalList.addAll(llist);
        }
        return finalList;
    }


    class MyComparator implements Comparator<String[]> {

        @Override
        public int compare(String[] o1, String[] o2) {
            return o1[9].compareTo(o2[9]);
        }
    }


    public void KerberosFresh_new(ClusterContext context){
        //初始化参数
        initFileInfo();

        String userName = context.getCommonProperties().getArgument("Cluster_User_Name","");
        String appendWhere=null;
        if (!StringUtils.isBlank(userName)){
            appendWhere=" and userinfo.userName ='"+userName+"'" ;
        }
        List<Map<String,Object>> configInfo=loadKerberosConfig(appendWhere);
        if(configInfo==null||configInfo.size()==0)return;
        //获取所有的机器信息
        Map<String,LinuxMachine> machineMap = Utils.getTotalMachineInfo();

        for (Map<String,Object> info : configInfo){
            String machineID=info.get("MachineId").toString();
            String authUser=info.get("username").toString(); //被认证的用户
            String kdcPassWord=info.get("pwd").toString(); //kdc认证密码
            LinuxMachine linuxMachine = machineMap.get(machineID);
            //添加用户
            linuxMachine.initOperation(OperationMarket.AddLinuxUser(userName));
            linuxMachine.initOperation(OperationMarket.AuthClientKDCPrinc_New(authUser,kdcPassWord));
            linuxMachine.initOperation(OperationMarket.ListClientKDCPrinc(authUser));
        }

        List<LinuxMachine> finalMachines = new ArrayList<LinuxMachine>();

        for(Map.Entry<String,LinuxMachine> entry : machineMap.entrySet()){
            if(entry.getValue().getOperationsSize()>0){
                finalMachines.add(entry.getValue());
            }
        }

        context.setMachineList(finalMachines);
        //刷新kerberos
        context.doTheThing();
        //这里会解析返回的结果，入库刷新信息
        List<String[]> allparams=parseSingleLog(context);
        //复制keytab文件到web
        copyKeyTabFileToLocal(allparams);

//        context.sysOutResult();
    }


    public List<String[]> parseSingleLog(ClusterContext context){

        List<String[]> all = new ArrayList<String[]>();
        List<String[]> allparams=new ArrayList<String[]>();//存放所有参数，给后面的cp 操作使用

        Map<String,String> userMap = Utils.getUserMapReserve(); //username-->id
        Map<String,String> linuxmachineMap = Utils.getCltReservMap(); //machineip--> id

        String webIP = getMachineByType(linuxMachines, LinuxMachine.MachineType.WebAPP).getSshAuthor().getHost();

        for(Map.Entry<String,List<LinuxResult>> entry:context.getContextResult().getAllResult().entrySet() ){
            for(LinuxResult result : entry.getValue()) {
                String stdOut =  result.getStdOut();
                System.out.println("==========start==========");
                System.out.println( stdOut);
                System.out.println("===========end=========");

                String[] rs=stdOut.split("\n");
                boolean isStart = false;
                boolean isTime = false;
                String path="" ;
                String privil="";
                String startTime="";
                String endTime="";
                String status="0";
                String machineIP=((SSHExecutor)result.getSshExecutor()).getSshAuthor().getHost();
                String machineid=linuxmachineMap.get(machineIP);

                String userid="";
                String username="";
                for(String line:rs){
                    if (line.startsWith("Ticket cache")) {
                        isStart = true;
                        if(line.split(":").length>=3)
                            path = line.split(":")[2];
                    }
                    if(isStart&&line.startsWith("Default principal")){
                        if(line.split(":").length>=2) {
                            privil = line.split(":")[1].trim();
                            username=privil.split("@")[0];
                            userid=userMap.get(username);
                        }
                    }
                    if(line.startsWith("Valid starting")){
                        isTime=true;
                    }
                    if (isTime && !line.contains("Valid starting")) {
                        //07/27/16 01:00:02  07/30/16 01:00:02  krbtgt/EXAMPLE.COM@EXAMPLE.COM^M
                        String[] tmp = line.split(" ");
                        if (tmp.length >= 5) {
                            startTime = Utils.parseStringToDate(tmp[0] + " " + tmp[1],"MM/dd/yy HH:mm:ss");
                            endTime = Utils.parseStringToDate(tmp[3] + " " + tmp[4],"MM/dd/yy HH:mm:ss");
                        } else {
                            status = "1";
                        }
                        isTime = false;
                    }

                }
                if(isStart) {
                    String[] vals = new String[]{userid, machineid, startTime, endTime, privil, path, status, System.currentTimeMillis() + ""};
                    String isLocal = "local";
                    if (!webIP.equals(machineIP))
                        isLocal = "remote";
                    String[] allVals = new String[]{userid, username, machineid, machineIP, startTime, endTime, privil, path, status, isLocal};
                    allparams.add(allVals);
                    all.add(vals);
                }
            }
        }

        //插入数据库
        String sql="insert into cluster_user_kbrauth " +
                "(userid,machineid,starttime,endtime,principal,ticketpath,status,createtime) " +
                "values (?,?,?,?,?,?,?,?)";
        String delSql="delete from cluster_user_kbrauth where userid=? and machineid=?";
        Object[][] o=new Object[all.size()][];
        Object[][] delParams = new Object[all.size()][];
        for(int i=0;i<all.size();i++){
            //插入先删除
            String[] strTmp=new String[]{all.get(i)[0],all.get(i)[1]};
            delParams[i]= strTmp;
            o[i]=all.get(i);
        }

        MysqlDBUtils.batchWebDB(delSql,delParams);
        MysqlDBUtils.batchWebDB(sql,o);

        return allparams;
    }












    public static void main(String[] args){
        KerberosService service =  new KerberosService();
//        service.KerberosFresh(" and userinfo.userName ='cc_test2'");

    }


}
